# -*- coding: utf-8 -*-
"""
Created on Mon Dec 15 15:29:27 2014

@author: wei
"""
from app import db
from app.models import *
import paramiko
from utils import log
import os
import sys
import re
import time
from imp import reload
import logging
from datetime import date

logger=log.get_logger('update',__name__)

__logging_date=date.today()

def reload_logger():
    global logger
    global __logging_date
    
    if __logging_date==date.today():#如果是当天的就不用重新加载logging了
        return
    
    __logging_date=date.today()
    
    logging.shutdown()
    reload(logging)
    logger=log.get_logger('update',__name__)

update_cmds_temp='''
export LANG="{lang}"
if [ ! -d "{branch_path_svn}" ]; then
    mkdir -p {branch_path};
    svn checkout {branch_svn_url} {branch_path} --force --no-auth-cache --username {svn_username} --password {svn_password}
    cd {branch_path}
    {init_cmds}
else
    svn revert -R {branch_path}
    svn update {branch_path} --no-auth-cache --username {svn_username} --password {svn_password}
fi
cd {branch_path}
{other_cmds}
'''

def get_ssh(host,port=22,username='root',password=''):
    ssh=paramiko.SSHClient()
    ssh.load_system_host_keys()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try :
        ssh.connect(host,port,username,password)
        return ssh,''
    except Exception as e:
        return None,str(e)

def _get_remote_server_lang(ssh,sleepfunc):
    chan=ssh.invoke_shell()
    try:
        chan.send('locale\n')
        result=''
        try_time=0
        while True:
            sleepfunc(1)
                
            rs=chan.recv(1000).decode()
            d=dict(re.findall('(.+)=(.+)\r',rs))
            result=d.get('LANG')
            if result or try_time>5:
                break
            try_time+=1
            
        return result
    finally:
        chan.close()
        
def _deploy_stat(func):
    def update_status(dinfo,sleepfunc):
        try:
            update=db.update(Deploy_info).values(
                is_deploying =True,
                deploytime   =db.func.now()
                ).where(Deploy_info.id==dinfo.id)
            db.session.execute(update)
            db.session.commit() 
            
            func(dinfo,sleepfunc)
        finally:
            update=db.update(Deploy_info).values(
                is_deploying =False
                ).where(Deploy_info.id==dinfo.id)
            db.session.execute(update)
            db.session.commit() 
            
    return update_status

def _handle_log(reader,decoding,write_log,sleepfunc):
    r=b''
    buf=reader.read(500)
    while buf:
        n=0
        l=len(buf)
        for i in range(l):
            if buf[i]==10:#\n 回车符
                temp=r+buf[n:i]
                write_log(temp.decode(decoding).strip('\r'))
                r=b''
                n=i+1
                sleepfunc(0.02)
                
        if l==(i+1):
            r+=buf[n:]
            
        buf=reader.read(500)
        sleepfunc(0.02)
        
    if r:
        write_log(r.decode(decoding).strip('\r'))

    
#def exec_command(self, command, bufsize=-1,timeout = None):
#    chan = self._transport.open_session()
#    if timeout is not None:
#        chan.settimeout(timeout)
#    chan.exec_command(command)
#    stdin = chan.makefile('wb', bufsize)
#    stdout = chan.makefile('rb', bufsize)
#    stderr = chan.makefile_stderr('rb', bufsize)
#    return stdin, stdout, stderr
    
@_deploy_stat
def deploy_branch(dinfo,sleepfunc):
    if not dinfo.server_ids:
        raise Exception('分枝[%s]未设置部署服务器!' % dinfo.branch)
        
    svn_username=Configs.get_value('SVN_USERNAME')
    svn_password=Configs.get_value('SVN_PASSWORD')
    repos_name=dinfo.repository_name
    
    repos_svn_url=os.path.join(Configs.get_value('SVN_URL'),repos_name)#仓库SVN路径
    
    branch_svn_url=os.path.join(repos_svn_url,dinfo.branch) if dinfo.branch!='/' else repos_svn_url
    #部署地址
    branch_path=dinfo.deploy_path.replace('<repos>',repos_name).replace('<branch>',dinfo.branch if dinfo.branch!='/' else '')

    branch_path_svn=os.path.join(branch_path,'.svn')#通过SVN目录判断

    servers=Servers.query.filter(Servers.id.in_(dinfo.server_ids.split(',')))
    for svr in servers:
        ssh,err=get_ssh(svr.host,svr.port,svr.user,svr.pwd)
        if not ssh:
            logger.error('无法连接到服务器[%s]:%s' % (svr.host,err))
            continue
        
        lang=_get_remote_server_lang(ssh,sleepfunc)
        if not lang:
            logger.error('无法取得服务器[%s]的语言/编码设置！' % svr.host)
            continue

        decoding=lang.split('.')[1]
        update_cmds=update_cmds_temp.format(
            lang=lang,
            branch_path_svn=branch_path_svn,
            branch_path=branch_path,
            branch_svn_url=branch_svn_url,
            svn_username=svn_username,
            svn_password=svn_password,

            init_cmds=dinfo.init_cmds.replace('\r','') or '',
            other_cmds=dinfo.cmds.replace('\r','') or ''
        )
        
        #保存本次执行的更新命令
        #last_exec_cmds_file=os.path.join(os.path.abspath(os.path.dirname(__file__)),'last_exec_cmds.txt')
        #with open(last_exec_cmds_file,'w') as f:
        #    f.write(update_cmds)
            
        logger.info('更新服务器[%s]  %s  >>>' % (svr.host,dinfo.deploy_path) )
        try:
            stdin,stdout,stderr=ssh.exec_command(update_cmds)
            sleepfunc(1)
                
            _handle_log(stdout,decoding,logger.info,sleepfunc)
            _handle_log(stderr,decoding,logger.error,sleepfunc)
        finally:
            stdin.close()
            stdout.close()
            stderr.close()
            ssh.close()
        
def _check_branch_in_list(branch,changed_list):
    if branch=='/':
        return True
        
    result=False
    for _,changed_file in changed_list:
        if changed_file.startswith(branch):
            result=True
            break
    return result
    
def _run_cmd(cmd):
    f=os.popen(cmd)
    try:
        result=f.read()
    finally:
        f.close()
    return result    

def _do_deploy(repos,version=None):#用update部署
    #取得svn更新文件列表
    if version:
        cmd='svnlook changed -r %s %s' % (version,repos)
    else:
        cmd='svnlook changed %s' % repos#最近更新
    r=_run_cmd(cmd)
    l=r.split('\n')
    changed_list=[re.split(' +',r,1) for r in l if r]
    
    if not changed_list:
        logger.info('没有更新的内容!')
        return 
        
    repos_list=Repository.query.all()
    deployed=False
    for rep in repos_list:
        if repos.startswith(rep.location):
            for deploy_info in rep.deploy_infos:
                if _check_branch_in_list(deploy_info.branch,changed_list):#判断分枝是否有更新
                    try:
                        deployed=True
                        logger.info('分枝:%s' % deploy_info.branch)
                        deploy_branch(deploy_info,time.sleep)
                    except Exception as e:
                        logger.error('部署[%s]出错:%s' % (rep.name,str(e)))
    if not deployed:
        logger.info('没有部署的分枝!')
        
if __name__=='__main__':
    if len(sys.argv)==3:
        repos,version=sys.argv[1],sys.argv[2]
        repos_name=repos[repos.rfind('/')+1:]#仓库名
        #取得修改作者
        cmd='svnlook author -r %s %s' % (version,repos)
        r=_run_cmd(cmd)
        author=r.rstrip('\n').replace('\n',',')
        logger.info('***********************************************')
        logger.info('更新部署:%s 版本号:%s 修改作者:%s' % (repos_name,version,author))
        try:
            _do_deploy(repos,version)
        except Exception as e:
            logger.error('部署出错:%s' % str(e))
    else:
        logger.info('部署参数错误:%s' % repr(sys.argv))